package com.jch.dm6_active_object.example;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;


import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class DiskBasedRequestPersistence implements RequestPersistence {

    // 缓存文件的存储管理
    final SectionBasedDiskStorage storage = new SectionBasedDiskStorage();
    final static Logger log = LogManager.getLogger(DiskBasedRequestPersistence.class);

    @Override
    public void store(MMSDeliverRequest mmsDeliverRequest) {
        String[] fileNameParts = storage.apply4Filename(mmsDeliverRequest);
        File file = new File(fileNameParts[0]);
        try(ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(file));) {
            objOut.writeObject(mmsDeliverRequest);
        } catch (IOException e) {
            storage.decrementSectionFileCount(fileNameParts[1]);
            log.error("Failed to store request",e);
        }
    }

    class SectionBasedDiskStorage {

        private Deque<String> sectionNames = new LinkedList<>();

        // Key - value : 存储子目录名 - 子目录下缓存文件计数器
        private Map<String, AtomicInteger> sectionFileCountMap = new HashMap<>();

        private int maxFilesPerSection = 2000;
        private int maxSectionCount = 100;
        private String storageBaseDir = getWorkingDir("/ab");
        private final Object sectionLock = new Object();

        public SectionBasedDiskStorage() {
            File dir = new File(storageBaseDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
        }

        public String[] apply4Filename(MMSDeliverRequest request) {
            String sectionName;
            int iFileCount;
            String oldestSectionName = null;
            String[] fileName = new String[2];
            synchronized (sectionLock) {
                sectionName = getSectionName();
                AtomicInteger fileCount;
                fileCount = sectionFileCountMap.get(sectionName);
                iFileCount = fileCount.get();
                if (iFileCount >= maxFilesPerSection) {
                    if (sectionNames.size() >= maxSectionCount) {
                        oldestSectionName = sectionNames.removeFirst();
                    }
                    sectionName = makeNewSectionDir();
                    fileCount = sectionFileCountMap.get(sectionName);
                }
                iFileCount = fileCount.addAndGet(1);
            }
            fileName[0] = storageBaseDir + "/" + sectionName + "/" + new DecimalFormat("0000").format(iFileCount)
                    + "-" + request.getTimeStamp().getTime() / 1000 + "-" + request.getExpiry() + ".rq";
            fileName[1] = sectionName;
            if (null != oldestSectionName) {
                this.removeSection(oldestSectionName);
            }
            return fileName;
        }

        public void decrementSectionFileCount(String sectionName) {
            AtomicInteger fileCount = sectionFileCountMap.get(sectionName);
            if (null != fileCount) {
                fileCount.decrementAndGet();
            }
        }

        private boolean removeSection(String sectionName) {
            boolean result = true;
            File dir = new File(storageBaseDir + "/" + sectionName);
            for (File file : dir.listFiles()) {
                result = result && file.delete();
            }
            result = result && dir.delete();
            return result;
        }

        private String getSectionName() {
            String sectionName;
            if (sectionNames.isEmpty()) {
                sectionName = this.makeNewSectionDir();
            } else {
                sectionName = sectionNames.getLast();
            }
            return sectionName;
        }

        private String makeNewSectionDir() {
            String sectionName;
            SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss");
            sectionName = sdf.format(new Date());
            File dir = new File(storageBaseDir + "/" + sectionName);
            if (dir.mkdir()) {
                sectionNames.addLast(sectionName);
                sectionFileCountMap.put(sectionName, new AtomicInteger(0));
            } else {
                throw new RuntimeException("Cannot create section dir " + sectionName);
            }
            return sectionName;
        }
    }

    @Override
    public void close() throws IOException {

    }

    private String getWorkingDir(String subDir) {
        if (null == subDir) {
            subDir = "";
        }
        String dir = System.getProperty("user.dir");
        dir = dir.replaceAll("\\\\", "/");
        dir += "/out/" + subDir + "/";
        return dir;
    }
}
